package com.lzc.springcloud.user.config;

import com.lzc.springcloud.user.application.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint;
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationFailureHandler;
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
import org.springframework.security.web.server.authentication.logout.RedirectServerLogoutSuccessHandler;
import org.springframework.security.web.server.authentication.logout.SecurityContextServerLogoutHandler;
import org.springframework.session.ReactiveMapSessionRepository;
import org.springframework.session.ReactiveSessionRepository;
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;

@Configuration
@EnableWebFluxSecurity
public class WebFluxSecruityConfigurer {

//    @Autowired
//    UserDetailsServiceImpl userDetailsService;

//    @Bean
//    public MapReactiveUserDetailsService userDetailsService() {
//        return new MapReactiveUserDetailsService();
//    }

    @Bean
    RedirectServerLogoutSuccessHandler logoutSuccessHandler() {
        RedirectServerLogoutSuccessHandler rr = new RedirectServerLogoutSuccessHandler();
        rr.setLogoutSuccessUrl(URI.create("/"));
        return rr;
    }

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http
                .logout()
                .logoutHandler(new SecurityContextServerLogoutHandler())
                .logoutUrl("/logout")
                .logoutSuccessHandler(logoutSuccessHandler()).and()
                .authorizeExchange().pathMatchers("/**/login.html", "/", "/index", "/**/logout", "/**/gologin").permitAll()
                .anyExchange().authenticated().and()
                .httpBasic().and()
                .formLogin()
                .loginPage("/identity/login.html")
                .authenticationEntryPoint(new RedirectServerAuthenticationEntryPoint("/identity/login.html"))
                .authenticationFailureHandler(new RedirectServerAuthenticationFailureHandler("/identity/login.html"))
                .authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler("/identity/gologin")).and()
                .csrf().disable();
        return http.build();
    }


    /**
     * 去除spring-security默认的权限前缀ROLE_
     * @return
     */
    @Bean
    protected RoleVoter roleVoter() {
        RoleVoter voter = new RoleVoter();
        voter.setRolePrefix("");
        return voter;
    }

    /**
     * authenticatedVoter是为了支持IS_AUTHENTICATED这种认证
     * authenticatedVoter提供的3种认证，分别是：
     * IS_AUTHENTICATED_ANONYMOUSLY 允许匿名用户进入
     * IS_AUTHENTICATED_FULLY 允许登录用户进入
     * IS_AUTHENTICATED_REMEMBERED 允许登录用户和rememberMe用户进入
     * @return
     */
    @Bean
    protected AuthenticatedVoter authenticatedVoter() {
        return new AuthenticatedVoter();
    }

    /**
     * 决策管理器
     * Spring提供了3个决策管理器：
     * AffirmativeBased 一票通过，只要有一个投票器通过就允许访问
     * ConsensusBased 有一半以上投票器通过才允许访问资源
     * UnanimousBased 所有投票器都通过才允许访问
     * @return
     */
    @Bean
    protected AffirmativeBased accessDecisionManager() {
        List<AccessDecisionVoter<? extends Object>> voters = new ArrayList<>();
        voters.add(roleVoter());
        voters.add(authenticatedVoter());
        return new AffirmativeBased(voters);
    }

    /**
     * 跟踪活跃的session,统计在线人数,显示在线用户.
     * @return
     */
    @Bean
    public SessionRegistry sessionRegistry(){
        return new SessionRegistryImpl();
    }

    //    /**
//     * 允许用户同时在线数，有一个maximumSessions属性，默认是1。通过sessionRegistry判断用户数是否已经超过了最大允许数，若超过了，那么就让最近一个的session过期（让上一个用户强制下线）
//     * @return
//     */
//    @Bean
//    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
//        return new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry());
//    }
}
